import BeanUtils from "@/utils/bean-utils";

const cai = (function () {
    let Shadow = function (keyFetcher) {
        this._ = {fetcher: keyFetcher, items: []}
    }, copy = function (src, dst) {
        if (!src || !dst) return;
        if (arguments.length <= 2) {
            Object.assign (dst, src);
        } else {
            for (let i = 2; i < arguments.length; i ++) {
                let name = arguments[i];
                if (name in src) {
                    dst [name] = src [name];
                }
            }
        }
    }, getItems = function (shadow) {
        if (!BeanUtils.has (shadow, '_.items') || !shadow._.items.length) return [];
        let a = [];
        shadow._.items.forEach (i => a.push (i));
        return a;
    }, convert = function (parent, item) {
        if (item instanceof ServiceRouteTable) return item;
        if (item instanceof ServiceRouteFrame) return item;
        if (item instanceof ServiceRouteItem)  return item;
        if (item instanceof ServiceRouteMethod) return item;
        if (item instanceof ServiceRouteParameter) return item;

        if (parent instanceof ServiceRouteTable) return new ServiceRouteFrame (item);
        if (parent instanceof ServiceRouteFrame) return new ServiceRouteItem (item);
        if (parent instanceof ServiceRouteItem)  return new ServiceRouteMethod (item);
        if (parent instanceof ServiceRouteMethod) return new ServiceRouteParameter (item);
    }, name_fetcher = [name => name.name];
    Shadow.prototype = {
        __add__: function (obj, sort) {
            if (window.cai.debugging) {
                console.log ('=========================================================================================');
                console.log ('= this = ', this);
                console.log ('= this instanceof Shadow', this instanceof Shadow);
                console.log ('= this instanceof Nameable', this instanceof Nameable);
                console.log ('= this instanceof ServiceRouteTable', this instanceof ServiceRouteTable);
                console.log ('= this instanceof ServiceRouteFrame', this instanceof ServiceRouteFrame);
                console.log ('= this instanceof ServiceRouteItem', this instanceof ServiceRouteItem);
                console.log ('= this instanceof ServiceRouteMethod', this instanceof ServiceRouteMethod);
                console.log ('= this instanceof ServiceRouteParameter', this instanceof ServiceRouteParameter);
                console.log ('=========================================================================================');
                console.log ('\r\n');
            }

            // obj = convert (this, obj);
            if (obj instanceof ServiceRouteParameter) {
                // 参数比较特殊，需单独处理
                // 参数是可能没有名称的，这时也需要保存参数
                this._.items.push (obj);
            } else {
                let fetch = this._.fetcher, key = fetch (obj);
                if (key) {
                    this [key] = obj;
                    this._.items.push (obj);
                    if (sort !== false) {
                        // 若没有指定不排序
                        this._.items.sort ((a, b) => {
                            let k1 = fetch (a), k2 = fetch (b);
                            if (k1 === null || k1 === undefined) return -1;
                            if (k2 === null || k2 === undefined) return  1;
                            if (k1 < k2) return -1;
                            if (k1 > k2) return  1;
                            return 0;
                        });
                    }
                }
            }

            return obj;
        },
        __remove__: function (key) {
            if (key in this) {
                let obj = this[key], index = this._.items.indexOf (obj), tail = this._.items.length;
                if (index >= 0 && index < tail) {
                    for (let i = index; i < tail - 1; i ++) {
                        this._.items [i] = this._.items [i + 1];
                    }
                    delete this._.items [tail - 1];
                    this._.items.length --;
                }
                delete this [key];
                obj = null;
            }
        },
        sort : function (func) {
            if (this._ && this._.items && this._.items.length) {
                if (func && (typeof func === 'function')) {
                    this._.items.sort (func);
                } else {
                    this._.items.sort ();
                }
            }
        }
    };

    let Nameable = function (arg0, patch) {
        Shadow.apply (this, name_fetcher);
        if (typeof arg0 === 'string' || arg0 instanceof String) {
            this.name = arg0;
        } else if (arg0) {
            patch (this, arg0);
        }
    }, ServiceRouteParameter = function (arg0) {    // org.dreamwork.cai.srf.ParameterInfo
        Nameable.apply (this, [arg0, (target, obj) => {
            copy (obj, target, 'name', 'type', 'description');
        }]);
    }, ServiceRouteMethod = function (arg0) {   // stub of org.dreamwork.cai.srf.MethodInfo
        Shadow.apply (this, obj => obj.uuid);
        if (typeof arg0 === 'string' || arg0 instanceof String) {
            this.name = arg0;
        } else if (arg0) {
            let obj = arg0, target = this;
            copy (obj, target, 'name', 'method', 'type', 'uuid', 'description');
            if ('parameters' in obj) {
                obj.parameters.forEach (p => this.__add__ (new ServiceRouteParameter (p)));
            }
        }
    }, ServiceRouteItem = function (arg0) {     // org.dreamwork.cai.srf.ServiceRouteItem
        Nameable.apply (this, [arg0, (target, obj) => {
            copy (obj, target, 'name', 'description', 'types');
            if ('methods' in obj) {
                obj.methods.forEach (m => target.__add__ (new ServiceRouteMethod (m)));
            }
        }]);
    }, ServiceRouteFrame = function (arg0) {    // org.dreamwork.cai.srf.ServiceRouteFrame
        Nameable.apply (this, [arg0, (target, obj) => {
            copy (obj, target, 'namespace', 'port', 'udpPort', 'host', 'metric', 'description', 'timestamp', 'lastUpdate');
            if ('items' in obj) {
                Object.values (obj.items).forEach (item => target.__add__ (new ServiceRouteItem (item)));
            }
        }]);
    }, ServiceRouteTable = function () {        // stub of org.dreamwork.cai.srf.ServiceRouteTable
        Shadow.apply (this, [obj => obj.namespace]);
    };

    ServiceRouteTable.prototype = Object.assign ({}, Shadow.prototype, {
        add : function (obj, sort) {
            this.__add__ (obj, sort);
        },
        remove : function (key) {
            this.__remove__ (key);
        },
        update : function (srf) {
            if (srf.namespace in this) {
                this[srf.namespace] = srf;
                let index = -1, temp;
                for (let i = 0, n = this._.items.length; i < n; i ++) {
                    if (this._.items[i].namespace === srf.namespace) {
                        index = i;
                        break;
                    }
                }

                if (index >= 0) {
                    this._.items [index] = srf;
                }
            } else {
                this.add (srf);
            }
        }
    });
    ServiceRouteFrame.prototype = Object.assign ({}, Shadow.prototype, {
        toString: function () {
            return this.namespace + ' from ' + this.host + ':' + this.port + ' at ' + new Date (this.timestamp);
        }
    });
    ServiceRouteItem.prototype = Object.assign ({}, Shadow.prototype);
    ServiceRouteMethod.prototype = Object.assign ({}, Shadow.prototype);

    Object.defineProperty (ServiceRouteTable.prototype, 'frames', {
        get () { return getItems (this); }
    });
    Object.defineProperty (ServiceRouteFrame.prototype, "items", {
        get () { return getItems (this); }
    });
    Object.defineProperty (ServiceRouteItem.prototype, 'methods', {
        get () { return getItems (this); }
    });
    Object.defineProperty (ServiceRouteMethod.prototype, 'parameters', {
        get () { return getItems (this); }
    });

    return {
        Shadow: Shadow,
        ServiceRouteTable: ServiceRouteTable,
        ServiceRouteFrame: ServiceRouteFrame,
        ServiceRouteItem: ServiceRouteItem,
        ServiceRouteMethod: ServiceRouteMethod,
        ServiceRouteParameter: ServiceRouteParameter
    }
})();

export default cai;